#include "LogCollection.hpp"

#include <cstdint>
#include <cstdarg>
#include <execinfo.h>
#include <memory>
#include <mutex>
#include <string>
#include <signal.h>
#include <thread>


namespace LogCollector
{
static bool sIsInit { false };
static std::mutex sMutex;
static std::thread sDbusThread;
static std::thread sAsyncLogThread;
static int sExitFds[2];
static std::shared_ptr<Logger> sLogger { nullptr };

namespace fs = std::filesystem;


ReturnStatus addFlushTimer()
{
    if(sLogger->mMaxFlushTimeOut <= 0) {
        return ReturnStatus::INVALID_PARAM;
    }

    __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
        __getSelfThreadId(), __getCurrentTimestamp().c_str(),
        __FILE__, __LINE__, __func__,
        "addFlushTimer: timeout=%d ms", sLogger->mMaxFlushTimeOut);

    auto ret = sd_event_add_time_relative(sLogger->mEvent, nullptr, CLOCK_MONOTONIC,
        sLogger->mMaxFlushTimeOut * TIME_CONVERSION_BASE, 0,
        [](sd_event_source *aSource, uint64_t aUsec, void *aUserdata) {
            std::lock_guard lock(sMutex);

            if (sLogger->mCurrentBufferSize > 0) {
                __flushLog(sLogger.get(), nullptr, 0);
            }

            if (sLogger->mMaxFlushTimeOut > 0) {
                sd_event_source_set_time_relative(aSource, TIME_CONVERSION_BASE * sLogger->mMaxFlushTimeOut);
                sd_event_source_set_enabled(aSource, SD_EVENT_ON); //! 重新启用
            }
            return 0;
        }, nullptr
    );

    return (ret >= 0 ? ReturnStatus::SUCCESS : ReturnStatus::UNKNOWN_ERROR);
}

bool matchStartupSignal()
{
    //! 绑定相关signal<StartUp>的dbus-callback
    auto ret = sd_bus_match_signal(sLogger->mBus, nullptr,
        SYSTEMD_SERVICE_NAME, SYSTEMD_SERVICE_PATH, SYSTEMD_SERVICE_INTERFACE,
        "StartUp", [](sd_bus_message *m, void *userdata, sd_bus_error *ret_error){
            std::lock_guard lock(sMutex);

            __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__, "%s start Startup...", sLogger->mProcessName);

            //! 获取本进程在DBus上的唯一名字
            const char *unique_name = nullptr;
            auto ret = sd_bus_get_unique_name(sLogger->mBus, &unique_name);
            if (ret < 0) {
                __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                    __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                    __FILE__, __LINE__, __func__,
                    "sd_bus_get_unique_name failed: %s", strerror(-ret));
                return 0;
            }

            __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__,
                "sd_bus_get_unique_name: %s", unique_name);

            ret = sd_bus_call_method_async(sLogger->mBus, nullptr,
                SYSTEMD_SERVICE_NAME, SYSTEMD_SERVICE_PATH, SYSTEMD_SERVICE_INTERFACE,
                "registerClient", nullptr, nullptr, "s", unique_name);
            if (ret < 0) {
                __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                    __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                    __FILE__, __LINE__, __func__, "registerClient failed");
            }
            return 0;
        }, nullptr
    );

    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_bus_match_signal<matchClearAllSignal>: %s", strerror(-ret));
        return false;
    }

    return true;
}

bool matchClearAllSignal()
{
    //! 绑定相关signal<clearAll>的dbus-callback
    auto ret = sd_bus_match_signal(sLogger->mBus, nullptr,
        nullptr, SYSTEMD_SERVICE_PATH, SYSTEMD_SERVICE_INTERFACE,
        "clearAll", [](sd_bus_message *m, void *userdata, sd_bus_error *ret_error){
            std::lock_guard lock(sMutex);

            __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__,
                "%s start clearAll...", sLogger->mProcessName);

            __clearLogFile(sLogger.get());
            auto ret = sd_bus_call_method_async(sLogger->mBus, nullptr,
                LOCAL_HANDLER_NAME, LOCAL_HANDLER_PATH, LOCAL_HANDLER_INTERFACE,
                "clearAllCallback", nullptr, nullptr, "s", sLogger->mProcessName);
            if (ret < 0) {
                __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                    __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                    __FILE__, __LINE__, __func__,
                    "clearAllCallback failed: %s", strerror(-ret));
            }
            return 0;
        }, nullptr
    );
    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_bus_match_signal<matchClearAllSignal>: %s", strerror(-ret));
        return false;
    }

    return true;
}

bool matchFlushAllSignal()
{
    //! 绑定相关signal<clearAll>的dbus-callback
    auto ret = sd_bus_match_signal(sLogger->mBus, nullptr,
        nullptr, SYSTEMD_SERVICE_PATH, SYSTEMD_SERVICE_INTERFACE,
        "flushAll", [](sd_bus_message *m, void *userdata, sd_bus_error *ret_error){
            std::lock_guard lock(sMutex);

            __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__,
                "%s start flushAll...", sLogger->mProcessName);

            __flushLog(sLogger.get(), nullptr, 0);
            auto ret = sd_bus_call_method_async(sLogger->mBus, nullptr,
                LOCAL_HANDLER_NAME, LOCAL_HANDLER_PATH, LOCAL_HANDLER_INTERFACE,
                "flushAllCallback", nullptr, nullptr, "s", sLogger->mProcessName);
            if (ret < 0) {
                __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                    __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                    __FILE__, __LINE__, __func__,
                    "flushAllCallback failed: %s", strerror(-ret));
            }
            return 0;
        }, nullptr
    );
    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_bus_match_signal<matchClearAllSignal>: %s", strerror(-ret));
        return false;
    }

    return true;
}

void EnterEventLoop()
{
    if (!sLogger) {
        return;
    }

    //! 设置定时器，定时把缓冲区的日志写入文件
    addFlushTimer();

    auto ret = sd_event_loop(sLogger->mEvent);
    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_event_loop: %s", strerror(-ret));
        sLogger = nullptr;
        return;
    }

    close(sExitFds[0]);
    close(sExitFds[1]);
}

void quitEventLoop()
{
    pipe(sExitFds);
    sd_event_add_io(sLogger->mEvent, nullptr, sExitFds[0], EPOLLIN,
        [](sd_event_source *s, int fd, uint32_t revents, void *userdata) {
            char buf[1];
            read(fd, buf, 1); //! 消费唤醒信号
            sd_event_exit(sLogger->mEvent, 0);
            return 1;
    }, nullptr);
    write(sExitFds[1], "\0", 1);  //! 唤醒循环
}

bool setupDBus()
{
    //! 创建事件循环对象(内部自带epoll线程池)
    auto ret = sd_event_new(&sLogger->mEvent);
    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_event_new: %s", strerror(-ret));
        return false;
    }

    ret = sd_bus_open_system(&sLogger->mBus);
    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_bus_open_system: %s", strerror(-ret));
        return false;
    }

    //! 把dbus挂到事件循环中，框架自动处理读写
    ret = sd_bus_attach_event(sLogger->mBus, sLogger->mEvent, 0);
    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_bus_attach_event: %s", strerror(-ret));
        return false;
    }

    // 获取本进程在DBus上的唯一名字
    const char *unique_name = nullptr;
    ret = sd_bus_get_unique_name(sLogger->mBus, &unique_name);
    if (ret < 0) {
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "sd_bus_get_unique_name failed: %s", strerror(-ret));
        return false;
    }

    __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
        __getSelfThreadId(), __getCurrentTimestamp().c_str(),
        __FILE__, __LINE__, __func__,
        "sd_bus_get_unique_name: %s", unique_name);

    sd_bus_error error = SD_BUS_ERROR_NULL;
    sd_bus_message *reply = nullptr;
    ret = sd_bus_call_method(sLogger->mBus,
                           SYSTEMD_SERVICE_NAME,               /* service */
                           SYSTEMD_SERVICE_PATH,              /* path */
                           SYSTEMD_SERVICE_INTERFACE,     /* interface */
                           "registerClient",
                           &error,
                           &reply,
                           "s", unique_name);                           /* 传入唯一名字 */
    if (ret < 0) {
        //! 这里需要去监听一个服务起来时发送的信号
        __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "registerClient failed: %s", error.message);

        matchStartupSignal();
        sd_bus_error_free(&error);
    } else {
        bool flag;
        ret = sd_bus_message_read(reply, "b", &flag);
        sd_bus_message_unref(reply);
        sd_bus_error_free(&error);
        if (ret < 0) {
            __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__,
                "read: %s", strerror(-ret));
            return false;
        }
        
        if (!flag) {
            __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__,
                "Register failed");
            return false;
        }
    }

    //! 绑定相关signal的dbus-callback
    if (!matchClearAllSignal()) {
        return false;
    }

    if (!matchFlushAllSignal()) {
        return false;
    }
    

    //! 使用std::thread新建一个线程去运作eventloop
    sDbusThread = std::thread(EnterEventLoop);

    return true;
}

ReturnStatus init()
{
    std::lock_guard lock(sMutex);
    if (sIsInit) {
        return ReturnStatus::ALREADY_INIT;
    }

    sLogger = std::shared_ptr<Logger>(__createDefaultLogger());

    const auto config = __loadLogConfigureOne(sLogger->mProcessName);
    sLogger->mLevel = config.mSeverityLevel;
    sLogger->mMaxFlushTimeOut = config.mMaxFlushTimeOut;
    sLogger->mMaxFlushSize = config.mMaxFlushSize;
    sLogger->mMaxFileCount = config.mMaxFileCount;
    sLogger->mMaxFileSize = config.mMaxFileSize;
    sLogger->mFromLocal = config.mFromLocal;
    sLogger->mIsWriteToJournal = config.mIsWriteToJournal;

    if (!sLogger->mFromLocal) {
        sLogger = nullptr;
        return ReturnStatus::NOT_PROCESS_FROM_JOURNAL;
    }

    if (auto status = __initLogFile(sLogger.get()); status != ReturnStatus::SUCCESS) {
        sLogger = nullptr;
        return status;
    }

    sAsyncLogThread = std::thread(
        [] () {
            bool hasNew = false;
            ReturnStatus ret = ReturnStatus::SUCCESS;
            while (true) {
                StoreLogTask process;
                hasNew = sLogger->mQueue.dequeue(process);
                if (!hasNew) {
                    continue;
                }

                //! deInit will enqueue a nullptr task
                if (process.mTask == nullptr) {
                    break;
                }

                ret = process.mTask(sLogger.get(), process.mMessage);
                if (ret != ReturnStatus::SUCCESS) {
                    __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                        __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                        __FILE__, __LINE__, __func__,
                        "ASync to store log failed: %d", static_cast<int>(ret));
                    continue;
                }
            }
        }
    );

    if (!setupDBus()) {
        sLogger = nullptr;
        return ReturnStatus::BUS_ERROR;
    }

    sIsInit = true;

    __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
        __getSelfThreadId(), __getCurrentTimestamp().c_str(),
        __FILE__, __LINE__, __func__,
        "init success, process=%s", sLogger->mProcessName);

    return ReturnStatus::SUCCESS;
}

ReturnStatus deInit()
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
        __getSelfThreadId(), __getCurrentTimestamp().c_str(),
        __FILE__, __LINE__, __func__, "deInit start");

    // 获取本进程在DBus上的唯一名字
    const char *unique_name = nullptr;
    if (sd_bus_get_unique_name(sLogger->mBus, &unique_name) < 0) {
        return ReturnStatus::BUS_ERROR;
    }

    __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
        __getSelfThreadId(), __getCurrentTimestamp().c_str(),
        __FILE__, __LINE__, __func__,
        "sd_bus_get_unique_name: %s", unique_name);

    sd_bus_call_method(sLogger->mBus,
        SYSTEMD_SERVICE_NAME,               /* service */
        SYSTEMD_SERVICE_PATH,              /* path */
        SYSTEMD_SERVICE_INTERFACE,     /* interface */
            "unregisterClient",
            nullptr,
            nullptr,
            "s", unique_name
    );

    if (sDbusThread.joinable()) {
        quitEventLoop();
        sDbusThread.join();
    }

    if (sAsyncLogThread.joinable()) {
        //! Send nullptr method to stop thread
        sLogger->mQueue.enqueue(
            StoreLogTask{
                nullptr,
                ""
            }
        );
        sAsyncLogThread.join();
    }

    if (__flushLog(sLogger.get(), nullptr, 0) != ReturnStatus::SUCCESS) {
        __writeLogSync(sLogger.get(), Severity::Level::ERROR,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "flushLog failed during deInit");
    }

    sLogger = nullptr;
    sIsInit = false;

    return ReturnStatus::SUCCESS;
}

void Log(Severity::Level aLevel,
    const char* aFile, int aLine, const char* aFunc, const char* aFmt, ...)
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return;
    }

    va_list args;
    va_start(args, aFmt);
    if (static_cast<int>(aLevel) >= sLogger->mLevel) {
        //! args可能会被多次使用，需拷贝一份(args只能消费一次)
        va_list argsCopy;
        va_copy(argsCopy, args);
        __writeLogVargsAsync(sLogger.get(), aLevel, __getSelfThreadId(),
            __getCurrentTimestamp().c_str(), aFile, aLine, aFunc, aFmt, argsCopy);
        va_end(argsCopy);
    }

    if (sLogger->mIsWriteToJournal) {
        va_list argsCopy;
        va_copy(argsCopy, args);
        auto ret = __writeJournalVargs(aLevel, aFile, aLine, aFunc, aFmt, argsCopy);
        va_end(argsCopy);
    }

    va_end(args);
}

ReturnStatus flushLog()
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    return __flushLog(sLogger.get(), nullptr, 0);
}

ReturnStatus clearLogFile()
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    return __clearLogFile(sLogger.get());
}

ReturnStatus setServerityLevel(Severity::Level aLevel)
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    sLogger->mLevel = static_cast<int>(aLevel);

    return ReturnStatus::SUCCESS;
}

ReturnStatus setMaxFileSize(uint32_t aSize)
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    sLogger->mMaxFileSize = aSize;

    return ReturnStatus::SUCCESS;
}

ReturnStatus setMaxFileCount(uint8_t aCount)
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    sLogger->mMaxFileCount = aCount;

    return ReturnStatus::SUCCESS;
}

ReturnStatus setMaxFlushSize(uint8_t aSize)
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    sLogger->mMaxFlushSize = aSize;

    return ReturnStatus::SUCCESS;
}

ReturnStatus setMaxFlushTimeOut(uint16_t aTimeout)
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    sLogger->mMaxFlushTimeOut = aTimeout;

    return addFlushTimer();
}

ReturnStatus setIsWriteToJournal(bool aEnable)
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    sLogger->mIsWriteToJournal = aEnable;

    return ReturnStatus::SUCCESS;
}

ReturnStatus registerUnexpectedExitHandler()
{
    std::lock_guard lock(sMutex);
    if (!sLogger) {
        return ReturnStatus::NOT_INIT;
    }

    struct sigaction action;
    action.sa_sigaction = unexpectedExitHandler;
    //! 清除mask过滤器(会忽略掉mask里面的signal)
    sigemptyset(&action.sa_mask);
    action.sa_flags = SA_SIGINFO;

    static constexpr int signalArray[] {
        SIGSEGV, //! 段错误
        SIGABRT, //! abort()
        SIGFPE, //! 除零
        SIGILL, //! 非法指令
        SIGINT //! Ctrl+C
    };

    for (int i = 0; i < sizeof(signalArray) / sizeof(signalArray[0]); ++i) {
        if (sigaction(signalArray[i], &action, nullptr) == -1) {
            __writeLogAsync(sLogger.get(), Severity::Level::ERROR,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__,
                "Signal<%d> register failed", signalArray[i]);
            return ReturnStatus::UNKNOWN_ERROR;
        }
    }

    __writeLogAsync(sLogger.get(), Severity::Level::DEBUG,
        __getSelfThreadId(), __getCurrentTimestamp().c_str(),
        __FILE__, __LINE__, __func__,
        "register unexpectedExitHandler successfully");

    return ReturnStatus::SUCCESS;
}

void unexpectedExitHandler(int aSignal, siginfo_t* aInfo, void* aContext)
{
    {
        std::lock_guard lock(sMutex);
        if (!sLogger) {
            _exit(1);
        }

        __writeLogAsync(sLogger.get(), Severity::Level::CRITICAL,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "Catch Signal<%d>", aSignal);

        //！抓取简单调用栈
        void* array[SINGLE_BUFFER_SIZE];
        size_t size = backtrace(array, SINGLE_BUFFER_SIZE);
        char** traces;
        //! 把调用栈回溯转成可读文本
        traces = backtrace_symbols(array, size);

        //! 打印trace
        __writeLogAsync(sLogger.get(), Severity::Level::CRITICAL,
            __getSelfThreadId(), __getCurrentTimestamp().c_str(),
            __FILE__, __LINE__, __func__,
            "Backtrace:");
        for (size_t i = 0; i < size; ++i) {
            __writeLogAsync(sLogger.get(), Severity::Level::CRITICAL,
                __getSelfThreadId(), __getCurrentTimestamp().c_str(),
                __FILE__, __LINE__, __func__,
                "%s", traces[i]);
        }

        free(traces);
    }

    //! 关闭logger相关服务
    deInit();

    //! 用 _exit 防止信号处理后再次触发
    _exit(1); 
}

} // namespace LogCollector